package com.example.Android_test.helpers;

import android.util.Log;
import com.example.Android_test.R;
import com.example.Android_test.activity.Main;
import com.example.Android_test.specs.SetOfWordsSpec;
import com.example.Android_test.specs.TranslationVariantSpec;
import com.example.Android_test.specs.WordSpec;
import com.example.Android_test.util.FileUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserFactory;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Random;

/**
 * Created with IntelliJ IDEA.
 * User: dns
 * Date: 05.05.13
 * Time: 20:40
 * To change this template use File | Settings | File Templates.
 */
public class SOWEngine {
    public static final SOWEngine INSTANCE = new SOWEngine();

    private ArrayList<SetOfWordsSpec> setOfWordsArrayList = new ArrayList<SetOfWordsSpec>();
    private String selectedSetOfWordsID = null;

    private WordSpec previousTrainingWord;


    private SOWEngine() {
        reloadSOWs();
    }

    public synchronized void reloadSOWs() {
        HashMap<String, Integer> setsOfWordMap = new HashMap<String, Integer>();
        /*
        //TODO Add wordset files here!!!
         */
        setsOfWordMap.put("sow_ru_en_test.xml", R.raw.sow_ru_en_test);
        setsOfWordMap.put("sow_ru_en_figures.xml", R.raw.sow_ru_en_figures);
        setsOfWordMap.put("sow_ru_en_colors.xml", R.raw.sow_ru_en_colors);

       /* setsOfWordMap.put("sow_en_ru_test.xml", R.raw.sow_en_ru_test);
        setsOfWordMap.put("sow_en_ru_figures.xml", R.raw.sow_en_ru_figures);*/
        setsOfWordMap.put("sow_en_ru_colors.xml", R.raw.sow_en_ru_colors);

        for (String name : setsOfWordMap.keySet()) {
            // TODO remove comment
            //if (!FileUtils.isInternalFileExist(name)) {
            FileUtils.saveResourcesToInternalStorage(name, setsOfWordMap.get(name));
            //}
        }

        for (String name : setsOfWordMap.keySet()) {
            if (FileUtils.isInternalFileExist(name)) {
                SetOfWordsSpec setOfWords = xmlParseSetOfWordsFile(name);

                Log.d(Main.LOG_TAG, "currentHome=" + SettingsHelper.INSTANCE.getCurrentLanguage().getCode() + " sowHome=" + setOfWords.getSowHomeLanguage());

                if (!SettingsHelper.INSTANCE.getCurrentLanguage().getCode().equals(setOfWords.getSowHomeLanguage())) {
                    Log.d(Main.LOG_TAG, "SOW skiped = " + setOfWords.getSowName());
                    continue;
                }

                if (setOfWords != null && setOfWords.isThisSetOfWordsCorrect()) {
                    setOfWordsArrayList.add(setOfWords);
                    Log.i(Main.LOG_TAG, setOfWords.toString());
                    //Log.i(Main.LOG_TAG, "[Info]: set " + setOfWords.toString() + " loaded and correct");
                } else {
                    Log.e(Main.LOG_TAG, "Loaded set of word corrupted. `" + name + "`");
                }
            }
        }

        if (setOfWordsArrayList.size() == 1){
            selectSetOfWords(setOfWordsArrayList.get(0).getSowID());
        }

        //TODO remove this
        selectSetOfWords(setOfWordsArrayList.get(0).getSowID());

    }



    private SetOfWordsSpec xmlParseSetOfWordsFile(String fileName) {
        SetOfWordsSpec newSetOfWord = new SetOfWordsSpec();
        newSetOfWord.setSowFileName(fileName);

        try {
            XmlPullParserFactory factory = XmlPullParserFactory.newInstance();
            factory.setNamespaceAware(true);
            XmlPullParser xpp = factory.newPullParser();

            String xmlString;
            try {
                xmlString = FileUtils.loadFileToString(fileName);
            } catch (Exception e) {
                Log.e(Main.LOG_TAG, "FileUtils.loadFileToString ERROR " + e.getMessage());
                return null;
            }

            xpp.setInput(new StringReader(xmlString));

            while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
                switch (xpp.getEventType()) {
                    case XmlPullParser.START_DOCUMENT: break;
                    case XmlPullParser.START_TAG:
                        if (xpp.getName().equals("head")) {
                            xmlParseHeadSection(xpp, newSetOfWord);
                        }

                        if (xpp.getName().equals("word")) {
                            xmlParseWordSection(xpp, newSetOfWord);
                        }
                        break;
                    case XmlPullParser.END_TAG: break;
                    case XmlPullParser.TEXT: break;
                    default: break;
                }
                xpp.next();
            }

        } catch (Exception e) {
            Log.e(Main.LOG_TAG, "FileUtils.xmlParseSetOfWordsFile ERROR " + e.getMessage());
            return null;
        }

        return newSetOfWord;
    }

    private void xmlParseWordSection(XmlPullParser xpp, SetOfWordsSpec newSetOfWords) throws Exception {
        WordSpec newWord = new WordSpec();

        String lastTag = "";
        while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
            switch (xpp.getEventType()) {
                case XmlPullParser.START_TAG:
                    lastTag = xpp.getName();

                    if (xpp.getName().equals("translate-variant")) {
                        xmlParseTranslationVariantSection(xpp, newWord);
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if (xpp.getName().equals("word")) {
                        newSetOfWords.addWord(newWord);
                        return;
                    }
                    break;
                case XmlPullParser.TEXT:
                    if (xpp.getText().trim().length() > 0) {
                        if (lastTag.equals("item")) {
                            newWord.setWord(xpp.getText());
                        }
                        if (lastTag.equals("transcription")) {
                            newWord.setTranscription(xpp.getText());
                        }
                        if (lastTag.equals("descriptiontranslation")) {
                            newWord.setDescriptionTranslation(xpp.getText());
                        }
                        if (lastTag.equals("description")) {
                            newWord.setDescription(xpp.getText());
                        }
                        if (lastTag.equals("incontext")) {
                            newWord.setInContext(xpp.getText());
                        }
                        if (lastTag.equals("incontextTranslation")) {
                            newWord.setInContextTranslation(xpp.getText());
                        }
                        if (lastTag.equals("sound")) {
                            newWord.setSoundRes(xpp.getText());
                        }
                        if (lastTag.equals("rating")) {
                            newWord.setRating(Integer.parseInt(xpp.getText()));
                        }
                        if (lastTag.equals("showed")) {
                            newWord.setShowed(Integer.parseInt(xpp.getText()));
                        }
                    }
                    break;

                default: break;
            }
            xpp.next();
        }

        throw new Exception("xmlParseWordSection error");
    }

    private void xmlParseTranslationVariantSection(XmlPullParser xpp, WordSpec newWord) throws Exception {
        TranslationVariantSpec translationVariant = new TranslationVariantSpec();

        String lastTag = "";
        while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
            switch (xpp.getEventType()) {
                case XmlPullParser.START_DOCUMENT:
                    break;
                case XmlPullParser.START_TAG:
                    lastTag = xpp.getName();
                    break;
                case XmlPullParser.END_TAG:
                    if (xpp.getName().equals("translate-variant")) {
                        newWord.addTranslationVariant(translationVariant);
                        return;
                    }
                    break;
                case XmlPullParser.TEXT:
                    if (xpp.getText().trim().length() > 0) {
                        if (lastTag.equals("partofspeech")) {
                            translationVariant.setPartOfSpeech(xpp.getText());
                        }
                        if (lastTag.equals("translate")) {
                            translationVariant.setTranslate(xpp.getText());
                        }
                        if (lastTag.equals("exampleofuse")) {
                            translationVariant.setExampleOfUse(xpp.getText());
                        }
                    }
                    break;

                default: break;
            }
            xpp.next();
        }

        throw new Exception("xmlParseTranslationVariantSection error");
    }

    private void xmlParseHeadSection(XmlPullParser xpp, SetOfWordsSpec newSetOfWords) throws Exception {
        String lastTag = "";
        while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
            switch (xpp.getEventType()) {
                case XmlPullParser.START_TAG:
                    lastTag = xpp.getName();
                    if (xpp.getName().equals("availabletrainings")) {
                        xmlParseAvailableTrainingsSection(xpp, newSetOfWords);
                    }
                    break;
                case XmlPullParser.END_TAG:
                    if (xpp.getName().equals("head")) {
                        return;
                    }
                    break;
                case XmlPullParser.TEXT:
                    if (xpp.getText().trim().length() > 0) {
                        if (lastTag.equals("sowName")) {
                            newSetOfWords.setSowName(xpp.getText());
                        }
                        if (lastTag.equals("sowID")) {
                            newSetOfWords.setSowID(xpp.getText());
                        }
                        if (lastTag.equals("sowComment")) {
                            newSetOfWords.setSowComment(xpp.getText());
                        }
                        if (lastTag.equals("sowVersion")) {
                            newSetOfWords.setSowVersion(xpp.getText());
                        }
                        if (lastTag.equals("sowCreateddate")) {
                            newSetOfWords.setSowCreatedDate(xpp.getText());
                        }
                        if (lastTag.equals("sowUpdateddate")) {
                            newSetOfWords.setSowUpdatedDate(xpp.getText());
                        }

                        if (lastTag.equals("sowIcon")) {
                            newSetOfWords.setSowIcon(xpp.getText());
                        }
                        if (lastTag.equals("sowHomeLanguage")) {
                            newSetOfWords.setSowHomeLanguage(xpp.getText());
                        }
                        if (lastTag.equals("sowTargetLanguage")) {
                            newSetOfWords.setSowTargetLanguage(xpp.getText());
                        }
                    }
                    break;

                default: break;
            }
            xpp.next();
        }
    }

    private void xmlParseAvailableTrainingsSection(XmlPullParser xpp, SetOfWordsSpec newSetOfWords) throws Exception {
        String lastTag = "";
        while (xpp.getEventType() != XmlPullParser.END_DOCUMENT) {
            switch (xpp.getEventType()) {
                case XmlPullParser.START_TAG:
                    lastTag = xpp.getName();
                    break;
                case XmlPullParser.END_TAG:
                    if (xpp.getName().equals("availabletrainings")) {
                        return ;
                    }
                    break;
                case XmlPullParser.TEXT:
                    if (xpp.getText().trim().length() > 0) {
                        if (lastTag.equals("training")) {
                            newSetOfWords.addAvailableTrainingName(xpp.getText());
                        }
                    }
                    break;

                default: break;
            }
            xpp.next();
        }

        throw new Exception("xmlParseAvailableTrainingsSection error");
    }


    public ArrayList<SetOfWordsSpec> getSetOfWordsArrayList() {
        return setOfWordsArrayList;
    }

    public void selectSetOfWords(String setOfWordID) {
        for (SetOfWordsSpec setOfWords : setOfWordsArrayList) {
            if (setOfWords.getSowID().equals(setOfWordID)) {
                selectedSetOfWordsID = setOfWordID;
                Log.i(Main.LOG_TAG, "selectedSetOfWordsID = " + selectedSetOfWordsID);
            }
        }
    }

    public WordSpec getWordToTraining() throws Exception {
        SetOfWordsSpec setOfWords;
        try {
            setOfWords = getSelectedSetOfWord();
        } catch (Exception e) {
            Log.w(Main.LOG_TAG, "SOWEngine.getRandomWords ERROR " + e.getMessage());
            throw new Exception("No one set was selected");
        }

        float averageRating = 0;
        for (WordSpec wordSpec : setOfWords.getWords()) {
            averageRating += wordSpec.getRating();
        }
        averageRating = averageRating / setOfWords.getWords().size();

        ArrayList<WordSpec> wordSpecArrayList = new ArrayList<WordSpec>();
        for (WordSpec currentWord: setOfWords.getWords()) {
            if (currentWord.getRating() <= averageRating){/* &&
                    !currentWord.getWord().equals(previousTrainingWord.getWord())) {*/
                wordSpecArrayList.add(currentWord);
            }
        }

        if (wordSpecArrayList.size() == 0) throw new Exception("No words for training");

        Random rand = new Random();
        int nextNum = rand.nextInt(wordSpecArrayList.size());

        WordSpec result = wordSpecArrayList.get(nextNum);
        if (result == null) throw new Exception("no word for training");

        previousTrainingWord = result;
        return result;
    }

    public SetOfWordsSpec getSelectedSetOfWord() throws Exception {
        for (SetOfWordsSpec setOfWords : setOfWordsArrayList) {
            if (setOfWords.getSowID().equals(selectedSetOfWordsID)) {
                return setOfWords;
            }
        }
        throw new Exception("No one set was selected");
    }

    public SetOfWordsSpec getSetOfWordByID(String id) throws Exception {
        for (SetOfWordsSpec setOfWords : setOfWordsArrayList) {
            if (setOfWords.getSowID().equals(id)) {
                return setOfWords;
            }
        }
        throw new Exception("Set of words id incorrect - " + id);
    }

    public ArrayList<WordSpec> getRandomWords(WordSpec exceptWord, int count) throws Exception {
        SetOfWordsSpec setOfWords;
        try {
            setOfWords = getSelectedSetOfWord();
        } catch (Exception e) {
            Log.w(Main.LOG_TAG, "SOWEngine.getRandomWords ERROR " + e.getMessage());
            throw new Exception("No one set was selected");
        }

        ArrayList<WordSpec> wordSpecArrayList = new ArrayList<WordSpec>();
        for (WordSpec wordSpec : setOfWords.getWords()) {
            if (!wordSpec.equals(exceptWord)) {
                wordSpecArrayList.add(wordSpec);
            }
        }

        if (wordSpecArrayList.size() == 0) throw new Exception("No words for random");

        Random rand = new Random();

        ArrayList<WordSpec> randomWords = new ArrayList<WordSpec>();
        for (int i=0; i<count; i++) {
            int nextNum = rand.nextInt(wordSpecArrayList.size());
            randomWords.add(wordSpecArrayList.get(nextNum));
            wordSpecArrayList.remove(nextNum);
        }

        return randomWords;
    }

    public void changeWordRating(WordSpec word, int ball) {
        word.setRating(word.getRating() + ball);
        saveCurrentSetOfWords();
    }

    public WordSpec getOneRandomWord(WordSpec exceptWord) throws Exception {
        return getRandomWords(exceptWord, 1).get(0);
    }

    public void saveCurrentSetOfWords() {
        for (SetOfWordsSpec setOfWords : setOfWordsArrayList) {
            if (setOfWords.getSowID().equals(selectedSetOfWordsID)) {
                String xml = setOfWords.toString();
                FileUtils.saveStringToInternalStorage(setOfWords.getSowFileName(), xml);
            }
        }
    }

    public String getSetOfWordsName(String setOfWordID) throws Exception {
        for (SetOfWordsSpec setOfWords : setOfWordsArrayList) {
            if (setOfWords.getSowID().equals(setOfWordID)) {
                return setOfWords.getSowName();
            }
        }
        throw new Exception("There is not a set with the name " + setOfWordID);
    }

    public String getNameOfSelectedSetOfWords() throws Exception{
        String setOfWordsName;

        try {
            setOfWordsName = getSetOfWordsName(selectedSetOfWordsID);
        } catch (Exception e) {
            throw new Exception("No one set was selected");
        }

        return setOfWordsName;
    }

    public String getSetOfWordsComment(String setOfWordID) {
        for (SetOfWordsSpec setOfWords : setOfWordsArrayList) {
            if (setOfWords.getSowID().equals(setOfWordID)) {
                return setOfWords.getSowComment();
            }
        }
        return null;
    }

    public String getCommentOfSelectedSetOfWords() {
        return getSetOfWordsComment(selectedSetOfWordsID);
    }

    private HashSet<String> getAvailableTrainings(String setOfWordID) {
        for (SetOfWordsSpec setOfWords : setOfWordsArrayList) {
            if (setOfWords.getSowID().equals(setOfWordID)) {
                return setOfWords.getAvailableTrainings();
            }
        }
        return null;
    }

    private HashSet<String> getAvailableTrainingsOfSelectedSetOfWords() {
        return getAvailableTrainings(selectedSetOfWordsID);
    }

    /*public boolean isETRAvailableOnSelectedSetOfWords() {
        HashSet<String> strings = getAvailableTrainings(selectedSetOfWordsID);

    }*/

    public boolean isTrainingAvailableOnSelectedSet(String trainingName) {
        HashSet<String> stringsHashSet = getAvailableTrainings(selectedSetOfWordsID);
        for (String currentName: stringsHashSet) {
            if (currentName.equals(trainingName)) {
                return true;
            }
        }

        return false;
    }
}
